/*
 * Modbus.h
 *
 *  创建时间: 2020年5月5日
 *      作者: Alejandro Mera
 *  中文注释：Modbus 协议头文件，包含主要结构体、枚举、函数声明等
 */

#ifndef THIRD_PARTY_MODBUS_INC_MODBUS_H_
#define THIRD_PARTY_MODBUS_INC_MODBUS_H_

#include "ModbusConfig.h"
#include <inttypes.h>
#include <stdbool.h>
#include "FreeRTOS.h"
#include "cmsis_os.h"
#include "task.h"
#include "queue.h"
#include "timers.h"

// 硬件类型枚举
typedef enum
{
    USART_HW = 1,      // 串口硬件
    USB_CDC_HW = 2,    // USB CDC
    TCP_HW = 3,        // TCP
    USART_HW_DMA = 4,  // 串口DMA
} mb_hardware_t;

// 主从模式枚举
typedef enum
{
    MB_SLAVE = 3,   // 从机
    MB_MASTER = 4   // 主机
} mb_masterslave_t;

// 地址模式枚举
typedef enum
{
    ADDRESS_BROADCAST = 0,  //!< 广播模式 -> modH->u8Buffer[ID] == 0
    ADDRESS_NORMAL = 1,     //!< 普通模式 -> modH->u8Buffer[ID] > 0
} mb_address_t;

// Modbus功能码枚举
typedef enum MB_FC
{
    MB_FC_READ_COILS               = 1,   /*!< 读线圈/数字输出 */
    MB_FC_READ_DISCRETE_INPUT      = 2,   /*!< 读数字输入 */
    MB_FC_READ_REGISTERS           = 3,   /*!< 读寄存器/模拟输出 */
    MB_FC_READ_INPUT_REGISTER      = 4,   /*!< 读模拟输入 */
    MB_FC_WRITE_COIL               = 5,   /*!< 写单个线圈/输出 */
    MB_FC_WRITE_REGISTER           = 6,   /*!< 写单个寄存器 */
    MB_FC_WRITE_MULTIPLE_COILS     = 15,  /*!< 写多个线圈/输出 */
    MB_FC_WRITE_MULTIPLE_REGISTERS = 16   /*!< 写多个寄存器 */
} mb_functioncode_t;

// 环形缓冲区结构体
typedef struct
{
    uint8_t uxBuffer[MAX_BUFFER];
    uint8_t u8start;
    uint8_t u8end;
    uint8_t u8available;
    bool    overflow;
} modbusRingBuffer_t;

// 报文帧各字段索引
typedef enum MESSAGE
{
    ID = 0,        //!< 设备ID
    FUNC,          //!< 功能码
    ADD_HI,        //!< 地址高字节
    ADD_LO,        //!< 地址低字节
    NB_HI,         //!< 数量高字节
    NB_LO,         //!< 数量低字节
    BYTE_CNT       //!< 字节计数
} mb_message_t;

// 通信状态
typedef enum COM_STATES
{
    COM_IDLE = 0,      // 空闲
    COM_WAITING = 1,   // 等待
} mb_com_state_t;

// 错误与操作码
typedef enum ERR_OP_LIST
{
    // 错误码
    ERR_NOT_MASTER      = 10,
    ERR_POLLING         = 11,
    ERR_BUFF_OVERFLOW   = 12,
    ERR_BAD_CRC         = 13,
    ERR_EXCEPTION       = 14,
    ERR_BAD_SIZE        = 15,
    ERR_BAD_ADDRESS     = 16,
    ERR_TIME_OUT        = 17,
    ERR_BAD_SLAVE_ID    = 18,
    ERR_BAD_TCP_ID      = 19,
    // 操作码
    OP_OK_QUERY         = 20  // 非错误，仅用于FreeRTOS通知返回非零，区分超时
} mb_err_op_t;

// Modbus异常码
enum
{
    EXC_FUNC_CODE = 1,   // 功能码异常
    EXC_ADDR_RANGE = 2,  // 地址范围异常
    EXC_REGS_QUANT = 3,  // 寄存器数量异常
    EXC_EXECUTE = 4      // 执行异常
};

// 字节域联合体
typedef union {
    uint8_t  u8[4];
    uint16_t u16[2];
    uint32_t u32;
} bytesFields;

// 主机查询结构体
typedef struct
{
    uint8_t u8id;                  /*!< 从机地址 1~247，0为广播 */
    mb_functioncode_t u8fct;       /*!< 功能码 */
    uint16_t u16RegAdd;            /*!< 首寄存器地址 */
    uint16_t u16CoilsNo;           /*!< 线圈/寄存器数量 */
    uint16_t *u16reg;              /*!< 主机内存映像指针 */
    uint32_t *u32CurrentTask;      /*!< 接收Modbus通知的任务指针 */
#if ENABLE_TCP == 1
    uint32_t   xIpAddress;
    uint16_t u16Port;
    uint8_t  u8clientID;
#endif
} modbus_t;

#if ENABLE_TCP == 1
typedef struct
{
    struct netconn *conn;
    uint32_t aging;
} tcpclients_t;
#endif

// Modbus句柄结构体
typedef struct
{
    mb_masterslave_t uModbusType;         // 主从类型
    UART_HandleTypeDef *port;             // 串口句柄
    uint8_t u8id;                         // 0=主机, 1..247=从机编号
    GPIO_TypeDef* EN_Port;                // 流控引脚端口
    uint16_t EN_Pin;                      // 流控引脚号
    mb_err_op_t i8lastError;              // 最后一次错误
    uint8_t u8Buffer[MAX_BUFFER];         // 通信缓冲区
    uint8_t u8BufferSize;                 // 缓冲区大小
    uint8_t u8lastRec;                    // 最后接收字节
    uint16_t *u16regs;                    // 寄存器指针
    uint16_t u16InCnt, u16OutCnt, u16errCnt; // 统计计数
    uint16_t u16timeOut;                  // 超时时间
    uint16_t u16regsize;                  // 寄存器区大小
    uint8_t dataRX;                       // 接收数据
    int8_t i8state;                       // 状态

    mb_address_t u8AddressMode;           // 地址模式

    // FreeRTOS 相关
    osMessageQueueId_t QueueTelegramHandle;   // 报文队列
    osThreadId_t myTaskModbusAHandle;         // Modbus任务
    xTimerHandle xTimerT35;                   // T35定时器
    xTimerHandle xTimerTimeout;               // 超时定时器
    osSemaphoreId_t ModBusSphrHandle;         // 信号量
    modbusRingBuffer_t xBufferRX;             // USART接收环形缓冲
    mb_hardware_t xTypeHW;                    // 硬件类型

#if ENABLE_TCP == 1
    tcpclients_t newconns[NUMBERTCPCONN];     // TCP连接
    struct netconn *conn;
    uint32_t xIpAddress;
    uint16_t u16TransactionID;
    uint16_t uTcpPort;                        // 仅用于从机
    uint8_t newconnIndex;
#endif

} modbusHandler_t;

// 响应、异常、校验和长度
enum
{
    RESPONSE_SIZE = 6,
    EXCEPTION_SIZE = 3,
    CHECKSUM_SIZE = 2
};

// 全局Modbus句柄数组
extern modbusHandler_t *mHandlers[MAX_M_HANDLERS];

// 函数声明
void ModbusInit(modbusHandler_t * modH);
void ModbusStart(modbusHandler_t * modH);

#if ENABLE_USB_CDC == 1
void ModbusStartCDC(modbusHandler_t * modH);
#endif

void setTimeOut(uint16_t u16timeOut);      // 设置通信超时
uint16_t getTimeOut();                     // 获取通信超时
bool getTimeOutState();                    // 获取超时状态
void ModbusQuery(modbusHandler_t * modH, modbus_t telegram);         // 队列尾部添加查询
uint32_t ModbusQueryV2(modbusHandler_t * modH, modbus_t telegram);  // 添加查询并等待通知
void ModbusQueryInject(modbusHandler_t * modH, modbus_t telegram);   // 队列头部插入查询
void StartTaskModbusSlave(void *argument); // 启动从机任务
void StartTaskModbusMaster(void *argument);// 启动主机任务
uint16_t calcCRC(uint8_t *Buffer, uint8_t u8length); // CRC计算

#if ENABLE_TCP == 1
void ModbusCloseConn(struct netconn *conn);                // 关闭TCP连接
void ModbusCloseConnNull(modbusHandler_t * modH);          // 关闭TCP连接并清理句柄
#endif

// 环形缓冲区相关函数
void RingAdd(modbusRingBuffer_t *xRingBuffer, uint8_t u8Val); // 添加字节
uint8_t RingGetAllBytes(modbusRingBuffer_t *xRingBuffer, uint8_t *buffer); // 取出所有字节
uint8_t RingGetNBytes(modbusRingBuffer_t *xRingBuffer, uint8_t *buffer, uint8_t uNumber); // 取出指定数量字节
uint8_t RingCountBytes(modbusRingBuffer_t *xRingBuffer); // 获取可用字节数
void RingClear(modbusRingBuffer_t *xRingBuffer); // 清空缓冲区

extern uint8_t numberHandlers; // 当前并发句柄数量

/* 原库未实现的函数声明

uint16_t getInCnt(); //!< 接收消息数
uint16_t getOutCnt(); //!< 发送消息数
uint16_t getErrCnt(); //!< 错误计数
uint8_t getID(); //!< 获取从机ID
uint8_t getState();
uint8_t getLastError(); //!< 获取最后错误
void setID(uint8_t u8id); //!< 设置从机ID
void setTxendPinOverTime(uint32_t u32overTime);
void ModbusEnd(); //!< 结束通信并释放串口

*/

#endif /* THIRD_PARTY_MODBUS_INC_MODBUS_H_ */
